﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description:  ParameterViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class ViewDefParamInfo
    {
        private string defaultInitialValue;
        private string deEscapedDefaultInitialValue;

        public bool IsRecommendedToBeParameterized;
        public ViewDefParameterType ViewDefParameterType;
        //Valid only for enum type 
        public List<string> Options;
        //Valid for all other types
        public string DefaultInitialValue
        { 
            get { return this.defaultInitialValue; }
            set
            {
                this.defaultInitialValue = value;
                if (this.defaultInitialValue != null)
                {
                    // For Example if ExitCodeRegEx should be defined in json as [[1-9][[0-9]*.
                    // But when user enters the value (either in Windows Azure Portal) or in Deploy Wizard for azure, the user has to enter [1-9][0-9]*
                    StringBuilder sb = new StringBuilder(this.defaultInitialValue);
                    List<ParameterizationHelper.ParsedParameterDetails> parsedParams;
                    List<int> escapedDelimiterLocations;
                    string error = ParameterizationHelper.ParseForParametersAndValidateSyntax(this.defaultInitialValue, null, false, true, out parsedParams, out escapedDelimiterLocations);
                    if (error == null)
                    {
                        // Remove the escaping for the delimeters
                        foreach (int escapedDelimiterLocation in escapedDelimiterLocations)
                        {
                            sb.Remove(escapedDelimiterLocation, 1);
                        }
                    }
                    this.deEscapedDefaultInitialValue = sb.ToString();
                }
            }
        }
     
        /// <summary>
        /// For Example if ExitCodeRegEx should be defined in json as [[1-9][[0-9]*.
        /// But when user enters the value (either in Windows Azure Portal) or in Deploy Wizard for azure, the user has to enter [1-9][0-9]*
        /// </summary>
        public string DeEscapedDefaultInitialValue
        {
            get { return this.deEscapedDefaultInitialValue; }
        }

        //Optional for StringParam       
        public int? MinLength;
        //Optional for StringParam       
        public int? MaxLength { get; set; }
        //Optional for StringParam
        public string RegExp { get; set; }
        //Optional for NumParam     
        public int? MinRange { get; set; }
        //Optional for NumParam        
        public int? MaxRange { get; set; }        
        //This is used for both label and others like  "is required", must be between, etc.               
        public string DefaultEnglishLabelText;       
        // Description is the same as param description in resdef
        //public string DefaultEnglishDescriptionText;
        //public string DefaultEnglishIsRequiredMessageText;

        public bool ResourceExtensionParameterReferenedInResourceDefinition { get; set; }

        public ViewDefParamInfo()
        {            
        }

        public ViewDefParamInfo(ViewDefParamInfo viewDefParamInfo)
        {
            this.IsRecommendedToBeParameterized = viewDefParamInfo.IsRecommendedToBeParameterized;
            this.ViewDefParameterType = viewDefParamInfo.ViewDefParameterType;
            if (viewDefParamInfo.Options != null)
            {
                this.Options = new List<string>(viewDefParamInfo.Options);
            }
            this.DefaultInitialValue = viewDefParamInfo.DefaultInitialValue;
            this.MinLength = viewDefParamInfo.MinLength;
            this.MaxLength = viewDefParamInfo.MaxLength;
            this.MinRange = viewDefParamInfo.MinRange;
            this.MaxRange = viewDefParamInfo.MaxRange;
            this.RegExp = viewDefParamInfo.RegExp;
            this.DefaultEnglishLabelText = viewDefParamInfo.DefaultEnglishLabelText;
        }

    }

    public class ParameterViewModel :ViewModelBase, IDataErrorInfo
    {
        private static readonly ObservableCollection<string> AvailableParameterTypes = new ObservableCollection<string>(Enum.GetNames(typeof(ParameterType)));    

        private readonly Parameter parameter;
        private string formattedName;
        private bool formattedNameDirty;
        private readonly ViewDefParamInfo viewDefParamInfo;
        private readonly TreeViewItemViewModel parametersContainerViewModel;
        //private readonly  List<Tuple<Func<string>, Action<string>>> propertiesUsingThisParameter  = new List<Tuple<Func<string>, Action<string>>>();

        public static class PropertyNames
        {
            public const string Name = "Name";
            public const string SelectedType = "SelectedType";
            public const string Type = "Type";
            public const string Description = "Description";
        }

        public ParameterViewModel(Parameter parameter, ViewDefParamInfo viewDefParamInfo, TreeViewItemViewModel parametersContainerViewModel)
        {
            this.parameter = parameter;
            this.viewDefParamInfo = viewDefParamInfo;
            this.parametersContainerViewModel = parametersContainerViewModel;
            if (!string.IsNullOrEmpty(this.parameter.Name))
            {
                this.formattedName = ParameterizationHelper.GenerateParameterString(this.parameter.Name);
            }
        }

        public static ViewDefParameterType GetViewDefParameterType(string paramType)
        {
            ViewDefParameterType viewDefParameterType;
            switch (paramType)
            {
                case "String":
                    viewDefParameterType = ViewDefParameterType.String;
                    break;
                case "MultiLineString":
                    viewDefParameterType = ViewDefParameterType.MultiLineString;
                    break;
                case "Int":
                    viewDefParameterType = ViewDefParameterType.Number;
                    break;
                case "Bool":
                case "Boolean":
                    viewDefParameterType = ViewDefParameterType.Boolean;
                    break;
                case "Credential":
                    viewDefParameterType = ViewDefParameterType.Credential;
                    break;
                case "SecureString":
                    viewDefParameterType = ViewDefParameterType.SecureString;
                    break;
                default:
                    viewDefParameterType = ViewDefParameterType.String;
                    break;
            }
            return viewDefParameterType;
        }

        public Parameter Parameter
        {
            get { return this.parameter; }
        }

        public string ParentContainerName
        {
            get
            {
                VmRoleResourceExtensionViewModel vmr = this.parametersContainerViewModel as VmRoleResourceExtensionViewModel;
                if (vmr != null)
                {
                    return vmr.Name;
                }
                VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;                       
                if (vmd != null)
                {
                    return vmd.Name;
                }
                return string.Empty;
            }
        }

        public ViewDefParamInfo ViewDefParamInfo
        {
            get { return this.viewDefParamInfo; }           
        }

        public string Name
        {
            get { return this.parameter.Name; }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    //foreach (Tuple<Func<string>, Action<string>> dependency in this.propertiesUsingThisParameter)
                    //{
                    //    ParameterizationHelper.RenameParameter(dependency.Item1, dependency.Item2, this.parameter.Name, value);
                    //}
                    this.parameter.Name = value;
                    this.formattedNameDirty = true;                        
                }
                this.OnPropertyChanged(PropertyNames.Name);
            }
        }

        public string FormattedName
        {
            get
            {
                if (this.formattedNameDirty)
                {
                    this.formattedName = ParameterizationHelper.GenerateParameterString(parameter.Name);  
                    this.formattedNameDirty = false;
                }
                return this.formattedName;
            }
        }
     
        public ObservableCollection<string> AvailableTypes
        {
            get
            {
                return AvailableParameterTypes;
            }
        }

        public string Type
        {
            get { return this.parameter.Type; }
        }

        public string SelectedType
        {
            get { return this.parameter.Type; }
            set
            {
                if (value == null)
                {
                    return;
                }
                if (this.Type == null || !string.Equals(value, this.Type, StringComparison.OrdinalIgnoreCase))
                {
                    this.viewDefParamInfo.ViewDefParameterType = GetViewDefParameterType(value);                  
                }
                this.parameter.Type = value;           
                this.OnPropertyChanged(PropertyNames.SelectedType);
                this.OnPropertyChanged(PropertyNames.Type);
            }
        }

        public string Description
        {
            get { return this.parameter.Description; }
            set
            {
                this.parameter.Description = value;
                this.OnPropertyChanged(PropertyNames.Description);
            }
        }

        /// <summary>
        /// This is only called when resext or resdef is opened and all the parameterizable properties are initialized
        /// </summary>
        /// <param name="info"></param>
        public void UpdateViewDefInfoFromParameterDefinitionContext(ViewDefParamInfo info)
        {
            if (info == null)
            {
                return;
            }
            // For the rest of the types, context provided by resext/resdef parameter type is more accurate. as the user could have made it a securestring instead.
            if (this.viewDefParamInfo.ViewDefParameterType == ViewDefParameterType.String)
            {
                this.viewDefParamInfo.ViewDefParameterType = info.ViewDefParameterType;
            }
            this.viewDefParamInfo.MaxLength = info.MaxLength;
            this.viewDefParamInfo.MinLength = info.MinLength;
            this.viewDefParamInfo.MaxRange = info.MaxRange;
            this.viewDefParamInfo.DefaultEnglishLabelText = info.DefaultEnglishLabelText;
            this.viewDefParamInfo.RegExp = info.RegExp;
            this.viewDefParamInfo.Options = info.Options;
            this.viewDefParamInfo.IsRecommendedToBeParameterized = info.IsRecommendedToBeParameterized;
            this.viewDefParamInfo.DefaultInitialValue = info.DefaultInitialValue;           
        }

        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.Name:
                        error = ParameterizationHelper.ValidateCannotBeParameterizedBySpecification(this.Name); 
                        if (string.IsNullOrEmpty(error))
                        {
                            VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;                       
                            if (vmd != null )
                            {
                                // check if viewdef is not referencing this property
                                if (vmd.ViewDefViewModel != null)
                                {
                                    if (!vmd.ViewDefViewModel.ParameterExistsInViewDef(parameter.Name))
                                    {
                                        error = string.Format("Add this parameter {0} to the ViewDefinition", this.Name);
                                    }                                   
                                }
                                else
                                {
                                    if (vmd.ResourceDefinitionStorageInfo.IsResdefPkg)
                                    {
                                        error = string.Format("Add ViewDefinition for Resource Definition Package and add this parameter {0} to it", this.Name);
                                    }                                   
                                }                           
                            }
                        }
                        break;
                    case PropertyNames.Type:
                        error = ValidationHelper.ValidateEnum(this.Type, typeof(ParameterType));   
                        if (string.IsNullOrEmpty(error))
                        {
                             VmRoleResourceDefinitionViewModel vmd = this.parametersContainerViewModel as VmRoleResourceDefinitionViewModel;                       
                            if (vmd != null )
                            {
                                // check if viewdef is not referencing this property
                                if (vmd.ViewDefViewModel != null)
                                {
                                    ViewDefParameter viewDefParameter = vmd.ViewDefViewModel.GetViewDefParameter(parameter.Name);                                
                                    if (viewDefParameter != null)
                                    {
                                        ViewDefParameterType viewDefParameterType;
                                        if (EnumHelper.TryParse(viewDefParameter.Type, true, out viewDefParameterType))
                                        {
                                            switch (viewDefParameterType)
                                            {
                                                case ViewDefParameterType.VMSize:
                                                case ViewDefParameterType.String:
                                                case ViewDefParameterType.OSVirtualHardDisk:
                                                case ViewDefParameterType.OSTimeZone:
                                                case ViewDefParameterType.Network:
                                                case ViewDefParameterType.Option:
                                                case ViewDefParameterType.MultiLineString:
                                                case ViewDefParameterType.DataVirtualHardDisk:
                                                case ViewDefParameterType.ComputerNamePattern:
                                                    if (!string.Equals(this.Type, ParameterType.String.ToString(), StringComparison.Ordinal))
                                                    {
                                                        error = string.Format("Parameter {0}'s Type in View Definition {1}. Type in Resource Definition {2}. Corresponding ResDef Type String", this.Name, viewDefParameter.Type, this.Type);
                                                    }
                                                    break;
                                                case ViewDefParameterType.Number:
                                                    if (!string.Equals(this.Type, ParameterType.Int.ToString(), StringComparison.Ordinal))
                                                    {
                                                        error = string.Format("Parameter {0}'s Type in View Definition {1}. Type in Resource Definition {2}. Corresponding ResDef Typee Int", this.Name, viewDefParameter.Type, this.Type);
                                                    }
                                                    break;
                                                case ViewDefParameterType.Boolean:
                                                    if (!string.Equals(this.Type, ParameterType.Boolean.ToString(), StringComparison.Ordinal))
                                                    {
                                                        error = string.Format("Parameter {0}'s Type in View Definition {1}. Type in Resource Definition {2}. Corresponding ResDef Type Boolean", this.Name, viewDefParameter.Type, this.Type);
                                                    }
                                                    break;
                                                case ViewDefParameterType.SecureString:
                                                    if (!string.Equals(this.Type, ParameterType.SecureString.ToString(), StringComparison.Ordinal))
                                                    {
                                                        error = string.Format("Parameter {0}'s Type in View Definition {1}. Type in Resource Definition {2}. Corresponding ResDef Type SecureString", this.Name, viewDefParameter.Type, this.Type);
                                                    }
                                                    break;
                                                case ViewDefParameterType.Credential:
                                                    if (!string.Equals(this.Type, ParameterType.Credential.ToString(), StringComparison.Ordinal) && !string.Equals(this.Type, ParameterType.SecureString.ToString(), StringComparison.Ordinal))
                                                    {
                                                        error = string.Format("Parameter {0}'s Type in View Definition {1}. Type in Resource Definition {2}. Corresponding ResDef Type is either Credential or SecureString", this.Name, viewDefParameter.Type, this.Type);
                                                    }
                                                    break;
                                            }                                                

                                        }
                                        else
                                        {
                                            error = string.Format("Parameter {0} View Definition parameter type is unrecognized", this.Name);
                                        }
                                    }                               
                                }                                                 
                            }
                        }
                        break;
                }
                return error;              
            }
        }

        public string Error { get; set; }
    }
}
